﻿using System;
using System.ComponentModel.Composition;
using System.Data.Services.Client;
using System.Linq;
using GiveAQuiz.Client.Data.DataServices;
using GiveAQuiz.QuizManager.Interfaces;
using System.Diagnostics;
using GiveAQuiz.Common;
using System.Collections.Generic;
using System.Net;
using System.Xml.Linq;
using System.Windows.Browser;

namespace GiveAQuiz.QuizManager.Model
{
  [Export(typeof(IQuizModel))]
  public class QuizModel : IQuizModel
  {
    GiveAQuizEntities _ctx;
    DataServiceCollection<QuizDefinition> _trackedDefinitions = null;
    DataServiceCollection<Quiz> _trackedQuizzes = null;

    public GiveAQuizEntities Context
    {
      get
      {
        if (_ctx == null)
        {
          _ctx = new GiveAQuizEntities(new Uri("/Services/QuizDataService.svc", UriKind.Relative), true);
        }
        return _ctx;
      }
    }


    public void GetQuizDefinitionAsync(int id, Action<QuizDefinition, Exception> callback)
    {
      var qry = from q in Context.QuizDefinitions
                                  .Expand("QuestionDefinitions")
                where q.Id == id
                select q;

      AppMessages.IsBusyMessage.Send(true);

      _trackedDefinitions = new DataServiceCollection<QuizDefinition>();
      _trackedDefinitions.LoadCompleted += (s, e) =>
        {
          try
          {
            if (e.Cancelled)
            {
              callback(null, new Exception("Load of Quiz was cancelled"));
            }
            else if (e.Error != null)
            {
              callback(null, StripDataServiceException(e.Error));
            }
            else
            {
              callback(_trackedDefinitions.FirstOrDefault(), null);
            }
          }
          finally
          {
            AppMessages.IsBusyMessage.Send(false);
          }
        };

      // Perform the query
      _trackedDefinitions.LoadAsync(qry);

    }

    public void SaveAsync(Action<Exception> callback)
    {
      AppMessages.IsBusyMessage.Send(true);

#if DEBUG
      DumpEntities();
#endif

      Context.BeginSaveChanges(SaveChangesOptions.Batch, r =>
        {
          try
          {
            var response = Context.EndSaveChanges(r);
            if (response != null && response.IsBatchResponse)
            {
              // Return the first error if there is one.
              var batchError = response.FirstOrDefault();
              callback(batchError == null ? null : batchError.Error);
            }
          }
          catch (Exception ex)
          {
            callback(StripDataServiceException(ex));
          }
          finally
          {
            AppMessages.IsBusyMessage.Send(false);
          }
        }, null);
    }


    public void AddQuizToModel(QuizDefinition quiz)
    {
      Context.AddToQuizDefinitions(quiz);
    }

#if DEBUG
    void DumpEntities()
    {
      foreach (var e in Context.Entities.Where(e => e.State != EntityStates.Unchanged))
      {
        Debug.WriteLine(string.Concat("Entity: ", e.Entity.GetType().Name, " State: ", e.State));
      }

      foreach (var e in Context.Links.Where(e => e.State != EntityStates.Unchanged))
      {
        Debug.WriteLine(string.Concat("Link: ", e.Source.GetType().Name, " State: ", e.State));
      }

    }
#endif



    public QuestionDefinition CreateQuestionDefinition(IQuestionType type)
    {
      var def = new QuestionDefinition()
                {
                  Id = 0,
                  QuestionTypeName = type.GetType().AssemblyQualifiedName,
                  SortOrder = 0
                };
      Context.AddToQuestionDefinitions(def);
      return def;

    }


    public void DeleteQuestionDefinition(QuestionDefinition def)
    {
      Context.DeleteObject(def);
    }

    public void CanTakeQuizAsync(int quizId, string email, Action<bool, Exception> callback)
    {
      WebClient client = new WebClient();

      client.DownloadStringCompleted += (s, e) =>
        {
          if (e.Error != null)
          {
            callback(false, e.Error);
          }
          else
          {
            XDocument doc = XDocument.Parse(e.Result);
            var element = doc.Element(XName.Get("CanTakeQuiz", "http://schemas.microsoft.com/ado/2007/08/dataservices"));
            callback(element == null ? false : element.Value == "true", null);
          }
        };
      var rawUrl = string.Format("QuizDataService.svc/CanTakeQuiz?quizId={0}&email='{1}'", quizId, HttpUtility.UrlEncode(email));

      client.DownloadStringAsync(new Uri(Context.BaseUri, rawUrl));
    }


    public void GetQuizAsync(int id, Action<Quiz, Exception> callback)
    {
      var qry = from q in Context.Quizzes
                                 .Expand("QuizDefinition/QuestionDefinitions")
                where q.Id == id
                select q;

      AppMessages.IsBusyMessage.Send(true);

      _trackedQuizzes = new DataServiceCollection<Quiz>();
      _trackedQuizzes.LoadCompleted += (s, e) =>
      {
        try
        {
          if (e.Cancelled)
          {
            callback(null, new Exception("Load of Quiz was cancelled"));
          }
          else if (e.Error != null)
          {
            callback(null, StripDataServiceException(e.Error));
          }
          else
          {
            callback(_trackedQuizzes.FirstOrDefault(), null);
          }
        }
        finally
        {
          AppMessages.IsBusyMessage.Send(false);
        }
      };

      // Perform the query
      _trackedQuizzes.LoadAsync(qry);
    }


    public void AddQuizAnswersToModel(IEnumerable<QuizAnswer> answers)
    {
      foreach (var answer in answers)
      {
        Context.AddToQuizAnswers(answer);
      }
    }


    public QuizResult CreateQuizResult(Quiz quiz, string name, string email, DateTime lastVisit)
    {
      // Add the QuizTaker
      var taker = GiveAQuiz.Client.Data.DataServices.QuizTaker.CreateQuizTaker(0, name, email, lastVisit);
      Context.AddToQuizTakers(taker);

      // Add the result
      var result = QuizResult.CreateQuizResult(0, quiz.Id, taker.Id);
      Context.AddToQuizResults(result);

      return result;
    }

    Exception StripDataServiceException(Exception ex)
    {
      if (ex is DataServiceClientException)
      {
        return ((DataServiceClientException)ex).InnerException;
      }
      return ex;
    }


  }
}
